package com.tulink.yichutu.controller.wxpay;

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.mxixm.fastboot.weixin.module.credential.WxJsTicketManager;
import com.mxixm.fastboot.weixin.module.message.WxUserMessage;
import com.mxixm.fastboot.weixin.service.WxApiService;
import com.tulink.yichutu.biz.*;
import com.tulink.yichutu.common.utils.DataResult;
import com.tulink.yichutu.common.utils.IPUtils;
import com.tulink.yichutu.entity.WxVipOrderEntity;
import com.tulink.yichutu.vo.req.OrderReqVO;
import com.tulink.yichutu.vo.resp.GoodsRespVO;
import com.tulink.yichutu.vo.resp.OrderRespVO;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author wcpaxx
 */
@RestController
@RequestMapping("/wxp")
@Slf4j
public class WxPayController {

  private final static String attach = "yichutu";

  @Value("${wx.pay.notifyUrl:}")
  private String notifyUrl;

  @Autowired
  private WxJsTicketManager wxJsTicketManager;
  @Autowired
  private WxPayService wxService;
  @Autowired
  private UserCacheBiz userCacheBiz;
  @Autowired
  private OrderBiz orderBiz;
  @Autowired
  private CashCouponBiz cashCouponBiz;
  @Autowired
  private WxApiService wxApiService;
  @Autowired
  private VipDateBiz vipDateBiz;
  @Autowired
  private GoodsBiz goodsBiz;
  @Autowired
  private WxBiz wxBiz;
  @Autowired
  private CmdBiz cmdBiz;

  ///**
  // * 配置公众号
  // *
  // */
  //@ApiOperation(value = "配置公众号")
  //@PostMapping("/wConfig")
  //public DataResult<GoodsRespVO> config() throws WxPayException {
  //  WxJsConfig wxJsConfig = wxJsTicketManager.getWxJsConfigFromRequest(
  //          WxJsApi.closeWindow,
  //          WxJsApi.chooseWXPay
  //          );
  //  return DataResult.success(wxJsConfig);
  //}

  /**
   * 获取商品列表
   *
   */
  @ApiOperation(value = "执行管理员命令")
  @GetMapping("/command")
  public String supperCommand(
          @RequestParam("code") String code,
          @RequestParam("command") String command) {
    if (StringUtils.isBlank(code) || StringUtils.isBlank(command)) {
      return "";
    }
    String openId = wxBiz.getOpenId(code);
    if (StringUtils.isBlank(openId) || !cmdBiz.isSupperOpenId(openId)) {
      return "";
    }
    return cmdBiz.command(command);
  }

  /**
   * 获取商品列表
   *
   */
  @ApiOperation(value = "获取商品列表")
  @PostMapping("/goodsList")
  public DataResult<GoodsRespVO> goodsList() {
    List<GoodsRespVO> goodsEntities = goodsBiz.getGoods().stream().map(p -> {
      GoodsRespVO respVO = new GoodsRespVO();
      respVO.setId(p.getId());
      respVO.setName(p.getName());
      respVO.setPrice(p.getPrice());
      return respVO;
    }).collect(Collectors.toList());
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("records", goodsEntities);
    return DataResult.success(jsonObject);
  }

  /**
   * 下单
   *
   * @param reqVO 下单请求参数
   */
  @ApiOperation(value = "下单")
  @PostMapping("/order")
  public DataResult<OrderRespVO> order(
          HttpServletRequest httpRequest,
          @Valid @RequestBody OrderReqVO reqVO
  ) throws WxPayException {
    try {
      if(StringUtils.isBlank(reqVO.getOpenId())) {
        reqVO.setOpenId(wxBiz.getOpenId(reqVO.getCode()));
      }
      if(StringUtils.isBlank(reqVO.getOpenId())) {
        log.info("未在公众号下单：{}", reqVO);
        return DataResult.fail("请通过公众号菜单打开此页面");
      }
      log.info("下单请求：{}", reqVO);
      if (!userCacheBiz.hasOpenId(reqVO.getOpenId())) {
        log.info("未曾登录过：{}", reqVO);
        return DataResult.fail("请先通过易出图扫码登录");
      }
      if (orderBiz.antiBrush(reqVO.getOpenId())) {
        log.info("触发防刷：{}", reqVO);
        return DataResult.fail("支付出现问题，代码：001，请加群反馈");
      }

      //创建商品订单
      WxVipOrderEntity orderEntity = orderBiz.createOrder(reqVO.getOpenId(), reqVO.getGoodsId());
      if (orderEntity == null) {
        log.info("订单创建失败：{}", reqVO);
        return DataResult.fail("支付出现问题，代码：002，请加群反馈");
      }

      //调用微信支付
      WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
      request.setVersion("1.0");
      request.setDeviceInfo("WEB");
      request.setTradeType(WxPayConstants.TradeType.JSAPI);
      request.setFeeType("CNY");
      request.setAttach(attach);
      request.setOpenid(reqVO.getOpenId());
      request.setProductId(orderEntity.getGoodsId());
      request.setOutTradeNo(orderEntity.getId());
      request.setTotalFee(
              orderEntity.getGoodsPrice()
                      .multiply(BigDecimal.valueOf(orderEntity.getGoodsCount() * 100))
                      .intValue());
      request.setSpbillCreateIp(IPUtils.getIpAddr(httpRequest));
      request.setNotifyUrl(notifyUrl);
      request.setReceipt("N");
      request.setBody("图连科技-易出图");
      LocalDateTime now = LocalDateTime.now();
      request.setTimeStart(now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
      LocalDateTime expireTime = now.plusMinutes(5);
      request.setTimeExpire(expireTime.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));

      WxPayMpOrderResult result = this.wxService.createOrder(request);
      OrderRespVO respVO = new OrderRespVO();
      BeanUtils.copyProperties(result, respVO);
      respVO.setOpenId(reqVO.getOpenId());
      orderBiz.putOrderCache(orderEntity, expireTime);
      log.info("订单创建成功：{}", reqVO);
      return DataResult.success(respVO);
    } catch (Exception ex) {
      log.error("创建订单出错", ex);
      return DataResult.fail("支付出现问题，代码：-1，请加群反馈");
    }
  }

  @ApiOperation(value = "支付回调")
  @PostMapping("/order/callback")
  public String orderCallback(@RequestBody String xmlData) throws WxPayException {
    String userId = null, openId = null;
    try {
      final WxPayOrderNotifyResult notifyResult = this.wxService.parseOrderNotifyResult(xmlData);
      if (!attach.equalsIgnoreCase(notifyResult.getAttach())) {
        log.warn("支付回调错误，非已出图订单: {}", notifyResult);
        return WxPayNotifyResponse.fail("不支持的业务");
      }
      if (!"Y".equalsIgnoreCase(notifyResult.getIsSubscribe())) {
        log.warn("支付回调错误，用户未订阅: {}", notifyResult);
        return WxPayNotifyResponse.fail("用户未关注公众号");
      }
      if (!WxPayConstants.TradeType.JSAPI.equalsIgnoreCase(notifyResult.getTradeType())) {
        log.warn("支付回调错误，不支持的交易类型: {}", notifyResult);
        return WxPayNotifyResponse.fail("支付类型错误");
      }
      String orderId = notifyResult.getOutTradeNo();
      WxVipOrderEntity orderEntity = orderBiz.getOrderCache(orderId);
      if (orderEntity == null) {
        log.warn("支付回调错误，订单不存在: {}", notifyResult);
        return WxPayNotifyResponse.fail("订单不存在");
      }
      openId = notifyResult.getOpenid();
      userId = orderEntity.getUserId();
      orderBiz.deal(orderEntity, notifyResult);
      if(notifyUser(orderEntity.getUserId(), notifyResult.getOpenid())) {
        log.info("支付成功: {}", notifyResult);
        return WxPayNotifyResponse.success("成功");
      } else {
        log.error("支付失败(-1): {}", notifyResult);
        return WxPayNotifyResponse.fail("支付失败(-1)");
      }
    } catch (DuplicateKeyException ex) {
      //幂等性
      if(StringUtils.isBlank(userId) || StringUtils.isBlank(openId)) {
        log.error("支付失败(-2): {}, {}", userId, openId);
        return WxPayNotifyResponse.fail("支付失败(-2)");
      }
      if(notifyUser(userId, openId)) {
        log.info("支付成功(重复调用): {}, {}", userId, openId);
        return WxPayNotifyResponse.success("成功");
      } else {
        log.error("支付失败(-3): {}, {}", userId, openId);
        return WxPayNotifyResponse.fail("支付失败(-3)");
      }
    } catch (Exception ex) {
      if(StringUtils.isNotBlank(openId)) {
        log.error("支付失败(-5): {}, {}", userId, openId);
        sendMessage("支付失败(-5)", openId);
      }
      return WxPayNotifyResponse.fail(ex.getMessage());
    }
  }

  private Boolean notifyUser(String userId, String openId) {
    try {
      Date endTime = vipDateBiz.getVipEndTime(userId);
      if (vipDateBiz.isLifetime(endTime)) {
        sendMessage("支付成功，您已经是终身会员！", openId);
        return true;
      } else if (vipDateBiz.isVip(endTime)) {
        sendMessage("支付成功，会员有效期为: " + VipDateBiz.getEndTimeFormat().format(endTime), openId);
        return true;
      } else {
        sendMessage("支付失败(-4)", openId);
        return false;
      }
    } catch (Exception ex) {
      log.error("notifyUser error", ex);
      return false;
    }
  }

  private void sendMessage(String msg, String openId) {
    wxBiz.sendUserMessage(
            WxUserMessage.textBuilder()
                    .content(msg)
                    .toUser(openId)
                    .build());
  }
}

